package org.xshop.service.impl;

import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.transaction.annotation.Transactional;
import org.xshop.core.JPush2Helper;
import org.xshop.mapper.TOrderExpressMapper;
import org.xshop.model.TExpressman;
import org.xshop.model.TExpressmanTrade;
import org.xshop.model.TOrder;
import org.xshop.model.TOrderAddress;
import org.xshop.model.TOrderExpress;
import org.xshop.model.TOrderFee;
import org.xshop.model.TStore;
import org.xshop.service.ITOrderExpressService;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.plugins.Page;
import com.weibo.api.motan.config.springsupport.annotation.MotanService;

import top.ibase4j.core.base.BaseServiceImpl;
import top.ibase4j.core.exception.BusinessException;
import top.ibase4j.core.support.Pagination;
import top.ibase4j.core.support.generator.Sequence;
import top.ibase4j.core.util.CacheUtil;
import top.ibase4j.core.util.DataUtil;
import top.ibase4j.core.util.DateUtil;
import top.ibase4j.core.util.ExceptionUtil;
import top.ibase4j.core.util.GaodeMapUtil;
import top.ibase4j.core.util.InstanceUtil;
import top.ibase4j.core.util.LocationUtil;
import top.ibase4j.core.util.PageUtil;

/**
 * Created by BaiJiFeiLong@gmail.com at 2018/4/4 16:25
 */
@MotanService(interfaceClass = ITOrderExpressService.class)
@CacheConfig(cacheNames = "TOrderExpress")
public class OrderExpressService extends BaseServiceImpl<TOrderExpress, TOrderExpressMapper>
implements ITOrderExpressService {
    private ExecutorService executorService = Executors.newCachedThreadPool();
    @Autowired
    private OrderService orderService;
    @Autowired
    private ExpressmanService expressmanService;
    @Autowired
    private ExpressmanTradeService expressmanTradeService;
    @Autowired
    private OrderAddressService orderAddressService;
    @Autowired
    private OrderFeeService feeService;
    @Autowired
    private StoreService storeService;
    @Autowired
    private SysService sysService;
    @Autowired
    private JPush2Helper jpushHelper;

    @Override
    public Pagination<Map<String, Object>> queryAvailableOrder(Map<String, Object> params) {
        Object orderBy = params.get("orderBy");
        logger.info("queryAvailableOrder params {}", JSON.toJSONString(params));
        if ("store".equals(orderBy) && DataUtil.isNotEmpty(params.get("lon"))
                && DataUtil.isNotEmpty(params.get("lat"))) {
            params.put("sortAsc", "Y");
            params.put("orderBy",
                "power(s.store_lon - " + params.get("lon") + ", 2) + power(s.store_lat-" + params.get("lat") + ", 2)");
        } else if ("address".equals(orderBy)) {
            params.put("sortAsc", "Y");
            params.put("orderBy", "power(a.lon_-s.store_lon, 2) + power(a.lat_-s.store_lat, 2)");
        } else if ("money".equals(orderBy)) {
            params.put("orderBy", "f.delivery_costs");
        } else {
            params.put("orderBy", "o.id_");
        }
        if (DataUtil.isNotEmpty(params.get("lon")) && DataUtil.isNotEmpty(params.get("lat"))) {
            BigDecimal lon = new BigDecimal(params.get("lon").toString());
            BigDecimal lat = new BigDecimal(params.get("lat").toString());
            params.put("minLon", lon.add(new BigDecimal("-0.1")));
            params.put("maxLon", lon.add(new BigDecimal("0.1")));
            params.put("minLat", lat.add(new BigDecimal("-0.1")));
            params.put("maxLat", lat.add(new BigDecimal("0.1")));
        }
        Page<Long> idPage = PageUtil.getPage(params);
        List<Long> ids = mapper.queryAvailableOrderId(idPage, params);
        Pagination<Map<String, Object>> page = new Pagination<Map<String, Object>>(idPage.getCurrent(),
                idPage.getSize());
        page.setTotal(idPage.getTotal());
        List<Map<String, Object>> records = InstanceUtil.newArrayList();
        for (Long id : ids) {
            records.add(getOrderInfo(id, params));
        }
        page.setRecords(records);
        return page;
    }

    @Override
    public Pagination<Map<String, Object>> queryOrder(Map<String, Object> params) {
        params.put("enable", "1");
        Object orderBy = params.get("orderBy");
        if ("store".equals(orderBy) && DataUtil.isNotEmpty(params.get("lon"))
                && DataUtil.isNotEmpty(params.get("lat"))) {
            params.put("sortAsc", "Y");
            params.put("orderBy",
                "power(s.store_lon - " + params.get("lon") + ", 2) + power(s.store_lat-" + params.get("lat") + ", 2)");
        } else if ("address".equals(orderBy)) {
            params.put("sortAsc", "Y");
            params.put("orderBy", "power(a.lon_-s.store_lon, 2) + power(a.lat_-s.store_lat, 2)");
        } else if ("money".equals(orderBy)) {
            params.put("orderBy", "f.delivery_costs");
        } else {
            params.put("orderBy", "e.id_");
        }
        Pagination<TOrderExpress> expressPage = super.query(params);
        Pagination<Map<String, Object>> page = new Pagination<Map<String, Object>>(expressPage.getCurrent(),
                expressPage.getSize());
        page.setTotal(expressPage.getTotal());
        List<Map<String, Object>> records = InstanceUtil.newArrayList();
        for (TOrderExpress orderExpress : expressPage.getRecords()) {
            Map<String, Object> map = getOrderInfo(orderExpress.getOrderId(), params);
            map.put("lastTime", 40 - DateUtil.getBetween(orderExpress.getCreateTime(), new Date()) / 60 % 40);
            records.add(map);
        }
        page.setRecords(records);
        return page;
    }

    private Map<String, Object> getOrderInfo(Long id, Map<String, Object> params) {
        Map<String, Object> map = InstanceUtil.newHashMap();
        TOrder order = new TOrder();
        order.setId(id);
        order = orderService.selectOne(order);
        TOrderFee fee = new TOrderFee();
        fee.setOrderId(order.getId());
        fee = feeService.selectOne(fee);
        TStore store = storeService.queryById(order.getStoreId());
        TOrderAddress address = new TOrderAddress();
        address.setOrderId(order.getId());
        address = orderAddressService.selectOne(address);
        map.put("orderId", order.getId());
        map.put("orderNo", order.getOrderNo());
        map.put("storeName", store.getStoreName());
        map.put("storePhone", store.getStorePhone());
        map.put("storeAddress", store.getStoreAddress());
        if (DataUtil.isNotEmpty(params.get("lon")) && DataUtil.isNotEmpty(params.get("lat"))) {
            BigDecimal lon = new BigDecimal(params.get("lon").toString());
            BigDecimal lat = new BigDecimal(params.get("lat").toString());
            map.put("storeDistance",
                new BigDecimal(LocationUtil.getDistance(lon, lat, store.getStoreLon(), store.getStoreLat()).toString())
                .divide(new BigDecimal("1000")).setScale(2, BigDecimal.ROUND_HALF_UP));
        } else {
            map.put("storeDistance", "0");
        }
        map.put("orderAddress", address.getAddressProvince() + address.getAddressCity() + address.getAddressArea()
        + address.getAddressDetail());
        map.put("orderPhone", address.getPhone());
        BigDecimal distance = new BigDecimal(LocationUtil
            .getDistance(address.getLon(), address.getLat(), store.getStoreLon(), store.getStoreLat()).toString())
                .divide(new BigDecimal("1000"));
        map.put("expressDistance", distance.setScale(2, BigDecimal.ROUND_HALF_UP));
        map.put("expressCosts", fee.getDeliveryCosts());
        map.put("orderTime", order.getOrderTime());
        map.put("expectTime", distance.divide(new BigDecimal(sysService.getSysParam("EXPRESS-SPEED", "15")), 2)
            .multiply(new BigDecimal("60")).setScale(0, BigDecimal.ROUND_HALF_UP));
        map.put("lastTime", 40 - DateUtil.getBetween(order.getOrderTime(), new Date()) / 60 % 40);
        return map;
    }

    @Override
    @Transactional
    public TOrderExpress update(TOrderExpress record) {
        String key = "ORDER-EXPRESS-" + record.getOrderId();
        String requestId = Sequence.next().toString();
        if (CacheUtil.getLock(key, requestId)) {
            try {
                TOrder order = orderService.queryById(record.getOrderId());
                if (record.getId() == null) {
                    if (Integer.valueOf(order.getState()) > 3) {
                        throw new BusinessException("该订单已经被抢单");
                    }
                    if (!"3".equals(order.getState())) {
                        throw new BusinessException("该订单还未支付,不能抢单");
                    }
                    order.setState("4");
                    orderService.updateOrderInfo(order);
                }

                if (record.getState() == 1) {
                    TStore store = storeService.queryById(order.getStoreId());
                    String max = sysService.getSysParam("EXPRESS-STORE-MAX", "500");
                    double distance = LocationUtil.getDistance(record.getLon(), record.getLat(), store.getStoreLon(),
                        store.getStoreLat());
                    if (distance > Double.valueOf(max)) {
                        throw new BusinessException("您还没有达店铺呢");
                    }
                } else if (record.getState() == 2) {
                    String max = sysService.getSysParam("EXPRESS-ADDRESS-MAX", "500");
                    double distance = LocationUtil.getDistance(record.getLon(), record.getLat(),
                        order.getAddress().getLon(), order.getAddress().getLat());
                    if (distance > Double.valueOf(max)) {
                        throw new BusinessException("您距离收货地址有点远哦");
                    }
                    order.setState("5");
                    orderService.updateOrderInfo(order);
                    TExpressman expressman = expressmanService.queryById(record.getExpressmanId());
                    expressman.setMoney(DataUtil.ifNull(expressman.getMoney(), new BigDecimal("0"))
                        .add(order.getFee().getDeliveryCosts()));
                    expressman.setScoure(DataUtil.ifNull(expressman.getScoure(), 0) + 1);
                    expressmanService.update(expressman);
                    TExpressmanTrade trade = new TExpressmanTrade();
                    trade.setExpressmanId(record.getExpressmanId());
                    trade.setTradeOrderNo(order.getOrderNo());
                    trade.setTradeType("1");
                    trade.setTradeMoney(order.getFee().getDeliveryCosts());
                    trade.setCreateBy(record.getExpressmanId());
                    trade.setUpdateBy(record.getExpressmanId());
                    expressmanTradeService.update(trade);
                }
                return super.update(record);
            } finally {
                CacheUtil.unLock(key, requestId);
            }
        } else {
            sleep(100);
            return update(record);
        }
    }

    @Override
    public Pagination<TOrderExpress> query(Map<String, Object> params) {
        params.put("enable", "1");
        params.put("orderBy", "e.id_");
        Pagination<TOrderExpress> page = super.query(params);
        for (TOrderExpress express : page.getRecords()) {
            express.setExpressman(expressmanService.queryById(express.getExpressmanId()));
        }
        return page;
    }

    @Override
    public Object getOrderChart(Map<String, Object> params) {
        Map<String, Object> result = InstanceUtil.newHashMap();
        BigDecimal lon = new BigDecimal(params.get("lon").toString());
        BigDecimal lat = new BigDecimal(params.get("lat").toString());
        params.put("minLon", lon.add(new BigDecimal("-1")));
        params.put("maxLon", lon.add(new BigDecimal("1")));
        params.put("minLat", lat.add(new BigDecimal("-1")));
        params.put("maxLat", lat.add(new BigDecimal("1")));
        List<Map<String, Object>> orderList = mapper.getOrderChart(params);
        List<Map<String, Object>> aList = InstanceUtil.newArrayList();
        List<Map<String, Object>> bList = InstanceUtil.newArrayList();
        List<Map<String, Object>> cList = InstanceUtil.newArrayList();
        ExecutorService executorService = Executors.newFixedThreadPool(50);
        for (Map<String, Object> map : orderList) {
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    Map<String, Object> location = GaodeMapUtil.getCoordinateByAddress(map.get("a").toString(),
                        map.get("p").toString());
                    map.remove("a");
                    map.remove("p");
                    map.put("lon", location.get("xIndex"));
                    map.put("lat", location.get("yIndex"));
                    int c = Integer.valueOf(map.get("c").toString());
                    if (c >= 300) {
                        aList.add(map);
                    } else if (c >= 100) {
                        bList.add(map);
                    } else {
                        cList.add(map);
                    }
                }
            });
        }
        executorService.shutdown();
        try {
            executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            logger.error(ExceptionUtil.getStackTraceAsString(e));
        }
        result.put("max", aList);
        result.put("mid", bList);
        result.put("min", cList);
        return result;
    }

    @Override
    public BigDecimal getExpMoney(Map<String, Object> params) {
        return mapper.getExpMoney(params);
    }

    @Override
    public void notification(Long orderId) {
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    Map<String, String> hashMap = InstanceUtil.newHashMap();
                    hashMap.put("type", "1");
                    String content = "有新订单";
                    List<String> ids = InstanceUtil.newArrayList();
                    BigDecimal max = new BigDecimal(sysService.getSysParam("EXPRESS-MSG-DISCOUNT", "3000"));

                    TOrderAddress entity = new TOrderAddress();
                    entity.setOrderId(orderId);
                    TOrderAddress address = orderAddressService.selectOne(entity);
                    Map<String, Object> params = InstanceUtil.newHashMap();
                    params.put("minLon", address.getLon().add(new BigDecimal("-0.1")));
                    params.put("maxLon", address.getLon().add(new BigDecimal("0.1")));
                    params.put("minLat", address.getLat().add(new BigDecimal("-0.1")));
                    params.put("maxLat", address.getLat().add(new BigDecimal("0.1")));
                    List<TExpressman> list = expressmanService.queryList(params);
                    logger.info("total {}", list.size());
                    for (int i = 0; i < list.size(); i++) {
                        try {
                            TExpressman record = list.get(i);
                            if (DataUtil.isNotEmpty(record.getRegistrationId()) && record.getOrderState() != null
                                    && record.getOrderState() == 1) {
                                BigDecimal distance = new BigDecimal(LocationUtil.getDistance(address.getLon(),
                                    address.getLat(), record.getLon(), record.getLat()));
                                logger.info("{} distance: {} ", record.getPhone(), distance);
                                if (distance.compareTo(max) <= 0) {
                                    String equipment = record.getRegistrationId().substring(2, 3);
                                    if ("0".equals(equipment)) {
                                        ids.add(record.getRegistrationId());
                                    } else {
                                        hashMap.put("sound", record.getSoundFile());
                                        jpushHelper.sendNotificationIOS("系统消息", content, hashMap,
                                            record.getRegistrationId());
                                    }
                                }
                            }
                        } catch (Exception e) {
                            logger.error(ExceptionUtil.getStackTraceAsString(e));
                        }
                    }
                    if (!ids.isEmpty()) {
                        try {
                            hashMap.remove("sound");
                            jpushHelper.sendNotificationAndroid("系统消息", content, hashMap, ids.toArray(new String[]{}));
                        } catch (Exception e) {
                            logger.error(ExceptionUtil.getStackTraceAsString(e));
                        }
                    }
                } catch (Exception e) {
                    logger.error(ExceptionUtil.getStackTraceAsString(e));
                }
            }
        });
    }
}
